.altmacro # 只有加了这一行才能用宏
.macro SAVE_GP n
    sd x\n, \n*8(sp)
.endm
.macro LOAD_GP n
    ld x\n, \n*8(sp)
.endm
    .section .text.trampoline
    .globl __alltraps
    .globl __restore
    .align 2 # 将 __alltraps 的地址 4 字节对齐, RISC-V 特权级规范的要求
__alltraps:
    csrrw sp, sscratch, sp # sscratch 寄存器作为中转
    # now sp->*TrapContext in user space, sscratch->user stack
    # save general-purpose registers
    sd x1, 1*8(sp)
    # skip sp(x2), we will save it later, see line 35~36
    sd x3, 3*8(sp)
    # skip tp(x4), application does not use it
    # save x5~x31
    .set n, 5
    .rept 27
        SAVE_GP %n
        .set n, n+1
    .endr
    # we can use t0/t1/t2 freely, because they were saved on kernel stack
    csrr t0, sstatus
    csrr t1, sepc
    sd t0, 32*8(sp) # save sstatus
    sd t1, 33*8(sp) # save sepc
    # read user stack from sscratch and save it on the kernel stack
    csrr t2, sscratch # read user sp
    sd t2, 2*8(sp)    # save user sp
    # load kernel_satp into t0
    ld t0, 34*8(sp)
    # load trap_handler into t1
    ld t1, 36*8(sp)
    # move to kernel_sp
    ld sp, 35*8(sp)
    # switch to kernel space
    csrw satp, t0
    # 刷新映射缓存
    sfence.vma
    # jump to trap_handler
    jr t1

__restore:
    # a0: *TrapContext in user space(Constant); a1: user space token
    # switch to user space
    csrw satp, a1
    # 刷新映射缓存
    sfence.vma
    csrw sscratch, a0
    mv sp, a0
    # now sp -> TrapContext in user space, start restoring based on it
    # 要先恢复 CSR 再恢复通用寄存器
    # 这样我们使用的三个临时寄存器 才能被正确恢复
    # 因为 t0->x5, t1->x6, t2->x7
    ld t0, 32*8(sp)
    ld t1, 33*8(sp)
    csrw sstatus, t0  # 恢复 sstatus 
    csrw sepc, t1     # 恢复 sepc, 现在 sepc 指向了位于 ecall 之后的第一条指令
    # restore general-purpuse registers except sp/tp
    ld x1, 1*8(sp)
    ld x3, 3*8(sp)
    .set n, 5
    .rept 27
        LOAD_GP %n
        .set n, n+1
    .endr
    # back to user stack
    ld sp, 2*8(sp)
    sret # 返回 U 特权级
